//ts中的class

//时刻记着 在ts中写数据，要写类型声明。  变量、函数、对象
//在class类中：
//父类  =  基类  =  超类
//子类  =  派生类
/*
    class类的一些知识点：
    1、class类的基本用法；
    2、class类的继承；
    3、ts中class的接口
    4、ts中class接口的继承
*/
//在ts当中class类的一个使用
class Person {
    //在ts中要时刻记着 数据类型声明
    name:string;
    age:number;
    //当在外侧声明了参数的数据类型，在constructor中也要写形参的数据类型声明，否则不会报错，但也失去了自动类型判断功能，无法判断传入的数据类型与规定的形参类型是否一致。
    constructor(name:string,age:number){
        this.name = name;
        this.age = age;
    }
    //原型对象上的方法
    jump(a:string):string{
        return a + this.name
    }
    //加到了所有的实例对象上
    sex:'男'
    //class自身上的方法
    static run(x:number):number{
        return x
    }
}

let tom = new Person('tom',20)
console.log(tom)

//如何调用原型对象上的方法
console.log(tom.jump('hello'))
//class类或者构造函数自身上的方法如何调用  
console.log(Person.run(800))

//2、在class类中使用接口
//使用接口 规定 类的数据类型
interface Anima{
    //规定的是class类中 原型对象上的方法 -- 函数
    //在class类的接口中，一般存放的都是 原型对象的类型声明
    sound(str:string):string;
    weight(num:number):number;
}
//代表 类Cat 要履行 Anima接口中的规定
class Cat implements Anima{
    sound(x:string):string{
        return x
    }
    weight(y:number):number{
        return y
    }
}
let kitty = new Cat();
console.log(kitty.sound('喵喵喵'));
console.log(kitty.weight(5));

//(二)一个类可以使用多个接口
//第一种方式：  接口写在一起
interface Anima1{
    color(x:string):string
}

class Catc implements Anima,Anima1{
    sound(str: string): string {
        return str
    }
    weight(num: number): number {
        return num
    }
    color(x: string): string {
        return x
    }
}

let tom1 = new Catc();
console.log(tom1.sound('喵喵喵'),tom1.weight(5),tom1.color('blue'))

//第二种方式： 接口可以继承  当写两个接口比较麻烦时
//将两个接口结合到一起
interface DogIn1{
    sound(x:string):string
}
interface DogIn2{
    weight(y:number):number
}
//接口的继承  注意：{}记得写
// interface DogInz extends DogIn1,DogIn2{}
interface DogInz extends DogIn1,DogIn2{
    color(x:string):string
}
class Dog implements DogInz{
    sound(x: string): string {
        return x
    }
    weight(y: number): number {
        return y
    }
    color(x: string): string {
        return x
    }
}
let jack = new Dog;
console.log(jack.sound('汪汪汪~'))
console.log(jack.weight(40))
console.log(jack.color('blank'))


//-------------------------------------------------------

//类的修饰符
/*
    public   默认值  公开的，哪里都可以访问（在 自身、子类、外部都可以访问的到）
    private  私有的  只有内部可以访问 （只能在自身中访问，在子类、外部都访问不到）
    protected  受保护的，只有内部和子类可以访问  （能在自身中、子类中可以访问，在外部访问不到）
    readonly  只读的  static  这两个也算是class类的修饰符
*/

//父类 基类 超类
class Book{
    public name:string;
    private price:string;//在外部中没有价格 不显示 ，在子类中 报错 不显示
    protected pages:number;
    constructor(name:string,price:string,pages:number){
        this.name = name;
        this.price = price;
        this.pages = pages;
    }
    sayBookName():string{
        //在类自身内部
        console.log(this.name)
        console.log(this.price)
        console.log(this.pages)
        return this.name
    }
}
let hongloumeng = new Book('红楼梦','200元',2500)
console.log(hongloumeng)
// console.log(hongloumeng.sayBookName())
console.log(hongloumeng.name)
// console.log(hongloumeng.price)  报错 此处代表不能外部访问
// console.log(hongloumeng.pages) 报错 此处代表不能外部访问
//子类 派生类
class HouBook extends Book{
    constructor(name:string,price:string,pages:number){
        super(name,price,pages)
    }
    test(){
        console.log(this.name)
        // console.log(this.price) 报错  代表此处父类的price，在子类中不能访问到
        console.log(this.pages)
        super.sayBookName()// name:雪中悍刀行  price:180元  2000 调用父类上的方法，sayBookName，  获取的是自身上的 name、price、pages
    }
}
let xiaoShuo = new HouBook('name:雪中悍刀行','price:180元',2000)
console.log(xiaoShuo)
xiaoShuo.test()



//测试  any 的 bug
//注意：一个有类型声明的变量，被类型为any的数据赋值时，该数据类型会被强制改变，改变为当时any的值的数据类型。例如 a:string  b:any = 1  a=b  console.log(a+'---'+typeof(a))  输出 1---number
//强制改变被赋值的变量的数据类型
//  数据类型为any的数据，可以被赋值为任意数据类型，也可以赋值给任意数据类型
//但是 类型声明为any的可以继续给其他类型数据赋值。
let aabb:any = 1;//数据类型为any的变量，可能一时随着所赋的值，类型变化，但是本质上他一直是any数据类型
let bbcc:string;
bbcc = aabb;
console.log(bbcc+'---'+typeof(bbcc)) // 1---number   string变量被强制改变成了number类型
console.log(aabb+'---'+typeof(aabb)) // 1---number
let ccdd:boolean;
ccdd = aabb; 
// ccdd = bbcc;  报错
console.log(ccdd) // 1---number   boolean类型的变量 被强制改变成了 number类型
aabb = '1';
let eeff:number;
eeff = aabb;
console.log(eeff+'---'+typeof(eeff)) //1---string    number类型的变量 又被 强制改变成了string类型

//测试unknown
//注意： unknown 可以被赋值为任意数据类型，但是不能赋值给任意数据类型，会报错，只能赋值给unknown类型的数据
let un1:unknown;
let str1:string = '字';
un1 = str1;
console.log(un1+'---'+typeof(un1)) //字---string
// let str2:string;
// str2 = un1   报错 unknown 不能赋值给任意数据类型
let un2:unknown;
un2 = un1;//  只能赋值给 unknown类型的变量
console.log(un2+'---'+typeof(un2))//字---string


//参数属性   当class类的修饰符写到 constructor的形参前，又叫做参数属性
//class类的修饰符 和 参数属性 都是针对于constructor的
class Monkey{
    //正常的 写法
    // name:string;
    // age:number;
    // constructor(name:string,age:number){
    //     this.name = name;
    //     this.age = age;
    // }

    //形参属性的写法
    constructor(public name:string, public age:number){
        this.name = name;
        this.age = age;
    }

    color(str:string):string{
        return str
    }
}

let jinsihou = new Monkey('金丝猴',8);
console.log(jinsihou);


//存取器   get 和 set      就是   getter和setter
class Tanger{
    //形参默认值
    name:string = '辛巴';
    age:number = 5;
    hobby:string = '吃饭、散步、睡觉';

    //get 读取值  info 是属性
    get info():string{
        return this.name + this.age +this.hobby
    }
    Color():string{
        return '黄色'
    }
    //set 检测改变值   当监测的info属性，值发生变化时，触发setter函数
    set info(value){
        console.log(value)
    }
}
let xinba = new Tanger();
console.log(xinba)
console.log(xinba.info); //注意 get 后面的info 是属性，不是方法
console.log(xinba.Color())
xinba.info = '辛巴6岁爱跑步'

//抽象类  abstract
/*
    抽象类不能创建实例，它包含的都是一些抽象的方法，主要时为子类做父类用；
    解析：
        1、不能具体化；
        2、包含的都是方法；（抽象的、不具体的、模糊的）
        3、主要在于传给子类
        抽象类，没有具体的实际的方法。就是规定子类中要有什么名字的方法，而方法具体操作什么都是可以的，但是如果没有规定的名字的方法，就会报错。
*/
//抽象类 Parent 父类
abstract class Parent {
    abstract mai();
    abstract play();
}
//子类一 Children1
class Children1 extends Parent{
    //当子类中 少一个父类规定的方法 都是会报错的
    mai() :string{
        return '买电脑！'
    }
    play():string{
        return '踢足球'
    }
}
let li = new Children1()
console.log(li.mai())
console.log(li.play())
//子类二 Children2
class Children2 extends Parent{
    mai():string{
        return '买摩托车！'
    }
    play():string{
        return '编程！'
    }
}
let sun = new Children2();
console.log(sun.mai());
console.log(sun.play());

export{
    tom
}